package bsh;

import bsh.exception.CompilationFailedException;
import bsh.meta.JavaClassWrapper;
import bsh.meta.Scope;
import bsh.meta.TypeWrapper;

/**
 * @author takeshi
 */
public class FormalParameterParser {

    /**
     * @param params node
     * @param scope the declaring {@link Scope}
     * @return {@link Parameter}
     * @throws CompilationFailedException e
     */
    public Parameter[] parseParameters(BSHFormalParameters params, Scope scope) throws CompilationFailedException {
        Parameter[] formalParams = new Parameter[params.jjtGetNumChildren()];
        for (int i = 0; i < formalParams.length; i++) {
            BSHFormalParameter arg = (BSHFormalParameter) params.getChild(i);
            formalParams[i] = this.extractParameter(arg, scope);
        }
        return formalParams;
    }

    /**
     * @param arg args
     * @param scope the declaring {@link Scope}
     * @return parameter
     * @throws CompilationFailedException e
     */
    private Parameter extractParameter(BSHFormalParameter arg, Scope scope) throws CompilationFailedException {
        TypeWrapper type = arg.jjtGetNumChildren() == 0 ? JavaClassWrapper.OBJECT_WRAPPER : extractType((BSHType) arg.getChild(0), scope);
        return new Parameter(type, arg.name);
    }

    /**
     * @param child child
     * @param scope the declaring {@link Scope}
     * @return {@link TypeWrapper}
     * @throws CompilationFailedException e
     */
    public static TypeWrapper extractType(BSHType child, Scope scope) throws CompilationFailedException {
        return TypedVariableDeclarationCompiler.resolveTypeForNode(child, scope);
    }

    /**
     * @param typeNode type
     * @return type
     * @param scope the declaring {@link Scope}
     * @throws CompilationFailedException e
     */
    public static TypeWrapper extractType(SimpleNode typeNode, Scope scope) throws CompilationFailedException {
        if (typeNode instanceof BSHType) {
            return extractType((BSHType) typeNode, scope);
        } else if (typeNode instanceof BSHPrimitiveType) {
            return JavaClassWrapper.unboxType(JavaClassWrapper.wrapperFor(((BSHPrimitiveType) typeNode).type));
        } else if (typeNode instanceof BSHAmbiguousName) {
            BSHAmbiguousName typeName = (BSHAmbiguousName) typeNode;
            final TypeWrapper resolveType = scope.resolveType(typeName.text);
            if (resolveType == null) {
                if ("var".equals(typeName.text)) {
                    return JavaClassWrapper.OBJECT_WRAPPER;
                }
                scope.resolveType(typeName.text);
                throw new CompilationFailedException(typeNode.getLineNumber(), "cannot find type: " + typeName.text);
            }
            return resolveType;
        } else if (typeNode instanceof BSHPrimaryExpression && (typeNode.jjtGetNumChildren() == 1)) {
            return extractType(typeNode.getChild(0), scope);
        } else {
            throw new CompilationFailedException(typeNode.getLineNumber(), "cannot find type");
        }
    }

}
